﻿#include "stdafx.h"
#include "AbstractSocket.h"

namespace TED {
#define DEFAULT_BUFFER_LENGTH	512 //기본 버퍼 Size

volatile ULONG sessionIndex =0;


AbstractSocket::AbstractSocket() {

}
AbstractSocket::AbstractSocket(SocketHandler* handler, const TCHAR *ip, const INT port) {
	this->handler = handler;
	this->port = port;
	this->ip = (TCHAR*) malloc(15 * sizeof(TCHAR));
//	_tcsncpy(this->ip, ip, _tcslen(ip));
	_tcscpy(this->ip, ip);
	address.sin_family = AF_INET;		// 주소체계(ipv4) 로설정, PF_INET : 프로토콜 체계
	address.sin_addr.s_addr = Util::ted_inet_addr(ip);		//  long 형 데이터의 바이트 오더를 바꾸어주는 함수, INADDR_ANY=localhost
	address.sin_port = htons(port);		// Host 시스템에서 Network로 short 형 데이터를 보낼 때  바이트 오더를 바꾸어주는 함수

	this->socketConfig = new SocketSessionConfig();
	// 임계구역
	//this->csLock = new CriticalSectionManager();
}

AbstractSocket::~AbstractSocket() {
	delete ip;
	delete socketConfig;	
	//delete csLock;
	WSACleanup();
}

// Socket 초기화
SOCKET AbstractSocket::Init() {
	int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if(result!=0) {
		Logger::Get().ErrorSocket(result, _T("WSAStartup Error"));
		return NULL;
	}

	// 소켓 생성 (성공시 핸들을, 실패시 "INVALID_SOCKET" 반환)
	// domain(AF_INET, PF_INET), type(SOCK_STREAM, SOCK_DGRM), protocol
	SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

	// 소켓 생성 실패 처리
	if (sock == INVALID_SOCKET) {
		closesocket(sock);
		Logger::Get().ErrorSocket(result, _T("Socket Error"));
		return NULL;
	}

	return sock;
}

IoSession& AbstractSocket::MakeSession(SOCKET sock) {
	csLock.EnterLock();
	IoSession* session = new DefaultIoSession(++sessionIndex, this, sock, this->socketConfig);
	csLock.LeaveLock();
	return *session;
}

SocketHandler& AbstractSocket::GetHandler() {
	return *handler;
}

// Socket을 Close 한다.
void AbstractSocket::Close(IoSession& session) {
	SOCKET socket = session.GetSocket();
	if(socket) {
		shutdown(socket, SD_SEND);
		closesocket(socket);		
	}

	handler->Close(session);

	// delete session memory;
	//delete &session;
}

bool AbstractSocket::Send(IoSession& session, IoBuffer& buf) {
	if(session.IsConnected()==false) return false;

	TCHAR buf_str[512];
	buf.GetHexDump(buf_str,512);
	Logger::Get().Debug(_T("Try to send. session(%d) buf=%s"), session.GetSocket(), buf_str);
	// 1. 소켓 디스크립터
	// 2. 전송할 데이터
	// 3. 데이터의 바이트 단위 길이
	// 4. flag : recv flag 참고
	INT result = send(session.GetSocket(), (char*)buf.toBuf(), buf.Limit(), 0);

	if (result == SOCKET_ERROR) {
		session.Close();
		return false;
	}

	buf.Rewind();
	handler->Sent(session, buf);
	return true;
}

// 데이터 수신
INT16 AbstractSocket::Recv(IoSession& session, IoBuffer* buf) {
	// 데이터를 받음 (message에 받은 데이터를 담음)
	// 4. flag( MSG_OOB : 긴급 데이터의 전송을 위한 옵션이다. ( send,recv)
	//          MSG_DONTROUTE : 전송시 routing 테이블을 참조하지 않는다.(로컬 네트웍 상에서  목적지를 찾을때 사용한다. (recv)
	//          MSG_PEEK : 입력버퍼에 수신된 데이터의 존재유무 확인을 위한 옵션, 읽어들인 정보를 입력 버퍼에서 삭제하지 않는다.(send)
	//          MSG_DONTWAIT : 입출력 함수 과정에서 블록킹을 하지 않는다. (send,recv)
	//          MSG_WAITALL : 요청한 바이트 수에 해당하는 데이터가 전부 수신될때까지 호출된 함수가 반환되는것을 막는다.
	//                              수신하는 데이터가 모두 도착할때까지 대기하도록 한다. (recv)
	// 0이 아니라 MSG_WAITALL로 하면 수신한 데이터가 깨진다.
	CHAR* recvBuffer = session.GetRecvBuffer();
	INT16 bufSize = socketConfig->GetRecvBufSize();
	ZeroMemory(recvBuffer, bufSize);
//	CHAR recvBuffer[512];

	bool existRecvBuf = buf!=NULL; //수신할 데이터가 전달되었는가.
	/*IoBuffer* buf=NULL;*/
	INT16 result = 0;
	INT16 total_length = 0;
	while(true) {		
		result=recv(session.GetSocket(), recvBuffer, bufSize, 0);

		if(result>0) {
			total_length += result;
			if(buf==NULL) {
				buf = new IoBuffer(socketConfig->GetRecvBufSize());
			}
			buf->AddBytes((BYTE*)recvBuffer, result);
			if(result<bufSize) {
				break;
			}
		// closed connection
		}else if(result==SOCKET_ERROR) {
			total_length =result;
			break;
		}else {
			break;
		}
	}	
	if(total_length>0) {
		buf->Flip();
		handler->Receive(session, *buf);		
	}

	if(existRecvBuf==false && buf!=NULL) {
		delete buf;
	}
	return total_length;
}

void AbstractSocket::Error(INT32 errorCode, const TCHAR* msg) {
	TCHAR content[512];
	ZeroMemory(content,512);
	Util::GetMessage(errorCode,content);

	Logger::Get().Debug(_T("%s%s"), msg, content);
}

int AbstractSocket::SetBlocking(SOCKET socket, bool blocking){
	// Set the mode of the socket to be nonblocking
	u_long iMode = !blocking; // 0:blocking, 1:non blocking

	int iResult = ioctlsocket(socket, FIONBIO, &iMode);	
	if(iResult==0) {
		Logger::Get().Debug(_T("set blocking=%d of socket=%d"), blocking,socket);
	}
	return iResult;
}
//SocketSessionConfig* AbstractSocket::GetSessionConfig() {
//	return this->socketConfig;
//}

}